/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::common::constants::*;
use crate::common::types::*;
use crate::{Box, log_e, log_i, Vec};
use crate::commands::common_command::{ScheduleInfo, AuthResultInfo, ExecutorSendMsg};
use crate::commands::auth_commands::AuthParam;
use crate::traits::user_db_manager::{CredentialFilter, Credential, UserDbManagerRegistry};
use crate::traits::context_manager::AuthContext;
use crate::contexts::base_auth_context::BaseAuthContext;
use crate::impls::default_config_manager::{NO_CHECK_PIN_EXPIRED_PERIOD, NO_SET_PIN_EXPIRED_PERIOD};
use crate::AuthIntent::AbandonedPinAuth;
use crate::traits::time_keeper::TimeKeeperRegistry;
use crate::vec;

pub struct PinAuthContext {
    auth_context: BaseAuthContext,
}

impl PinAuthContext {
    pub fn new(context_id: u64, auth_param: &AuthParam) -> Self {
        Self {
            auth_context: BaseAuthContext::new(context_id, auth_param),
        }
    }

    fn get_expired_info(&self, credential_id: CredentialId) -> Result<i64, ErrorCode> {
        if self.auth_context.auth_intent() == AuthIntent::AbandonedPinAuth {
            return UserDbManagerRegistry::get().get_credential_valid_peroid(credential_id);
        }

        let current_time = TimeKeeperRegistry::get().get_ree_time()? as i64;
        let expired_timestamp = self.auth_context.get_expired_time_stamp()?;
        if current_time < expired_timestamp {
            Ok(expired_timestamp - current_time)
        } else if !self.auth_context.is_expired_return_success() {
            Err(ErrorCode::PinExpired)
        } else {
            Ok(NO_SET_PIN_EXPIRED_PERIOD as i64)
        }
    }

    fn check_user_id(&self, user_id: i32) -> Result<(), ErrorCode> {
        if self.auth_context.user_id() == user_id {
            Ok(())
        } else {
            log_e!("user id mismatch: expected {}, got {}", self.auth_context.user_id(), user_id);
            Err(ErrorCode::GeneralError)
        }
    }

    // todo
    fn check_auth_trust_level(&self, _capability_level: AuthCapabilityLevel) -> Result<(), ErrorCode> {
        Ok(())
    }

    // todo
    fn check_abandon_flag(&self, credential_id: CredentialId) -> Result<(), ErrorCode> {
        let filter: CredentialFilter = Box::new(move |credential: &Credential| {
            credential.credential_id == credential_id
        });

        let credential_info = UserDbManagerRegistry::get().get_credential(filter)?;
        if !credential_info.abandon_flag() && self.auth_context.auth_intent() == AuthIntent::AbandonedPinAuth {
            return Err(ErrorCode::GeneralError)
        }

        if credential_info.abandon_flag() && self.auth_context.auth_intent() != AuthIntent::AbandonedPinAuth {
            return Err(ErrorCode::GeneralError)
        }

        Ok(())
    }

    // todo
    fn proc_re_enroll_flag(&self) -> () {
        return;
    }
}

impl AuthContext for PinAuthContext {
    fn schedule_id(&self) -> u64 {
        self.auth_context.schedule_id()
    }
    fn context_id(&self) -> u64 {
        self.auth_context.context_id()
    }
    fn begin(&mut self, auth_param: &AuthParam) -> Result<Vec<ScheduleInfo>, ErrorCode> {
        self.auth_context.check_operate_permission(auth_param)?;
        let user_id = auth_param.base_param.user_id;
        let auth_type = auth_param.auth_type;

        let filter: CredentialFilter = Box::new(move |credential: &Credential| {
            credential.user_id == user_id && credential.auth_type == auth_type
        });

        self.auth_context.generate_auth_schedule(auth_param, filter)?;
        let executor_message = self.auth_context.create_executor_message(&self.auth_context.auth_schedule())?;

        let mut schedule_info_list = Vec::new();
        let schedule_info = ScheduleInfo {
            schedule_id: self.auth_context.schedule_id(),
            template_ids: self.auth_context.template_id_list().to_vec(),
            auth_type,
            executor_matcher: self.auth_context.get_executor_matcher(auth_param)?,
            schedule_mode: ScheduleMode::Auth,
            executor_indexes: self.auth_context.get_executor_indexes(auth_param)?,
            executor_messages: vec![executor_message],
        };
        schedule_info_list.push(schedule_info);
        Ok(schedule_info_list)
    }

    fn update_result(&mut self, schedule_result: &Vec<u8>) -> Result<AuthResultInfo, ErrorCode> {
        let executor_result = self.auth_context.parse_executor_message(schedule_result)?;
        if executor_result.is_success() {
            log_e!("Executor authentication failed with result: {}", executor_result.result());
            return Err(ErrorCode::try_from(executor_result.result()).unwrap_or(ErrorCode::GeneralError));
        }

        self.check_auth_trust_level(executor_result.capability_level())?;
        let template_id = *executor_result.template_id();
        let user_id = UserDbManagerRegistry::get().get_user_id_by_template_id(template_id)?;
        self.check_user_id(user_id)?;

        let credential_id = UserDbManagerRegistry::get().get_credential_id_by_template_id(template_id)?;
        self.check_abandon_flag(credential_id)?;

        let pin_expired_info = self.get_expired_info(credential_id)?;
        let re_enroll_flag = executor_result.re_enroll_flag();
        if re_enroll_flag {
            self.proc_re_enroll_flag();
        }

        let auth_token = self.auth_context.generate_auth_token(user_id, credential_id)?;
        let executor_message_list = self.auth_context.create_executor_command(user_id, &executor_result)?;
        let executor_send_msgs: Vec<ExecutorSendMsg> = executor_message_list
            .into_iter()
            .map(|executor_msg| {
                ExecutorSendMsg {
                    executor_index: executor_msg.executor_index(),
                    command_id: executor_msg.auth_property_mode() as i32,
                    msg: executor_msg.executor_message().to_vec(),
                }
            })
            .collect();
        let auth_result_info = AuthResultInfo {
            result: executor_result.result(),
            lockout_duration: executor_result.freezing_time(),
            remain_attempts: executor_result.remain_attempts(),
            msgs: executor_send_msgs,
            token: auth_token,
            root_secret: executor_result.root_secret().clone(),
            user_id,
            credential_id,
            pin_expired_info,
            remote_auth_result_msg: Vec::new(),
            re_enroll_flag,
        };
        Ok(auth_result_info)
    }
}
